#include <accent/algorithms/concat.hpp>
#include <accent/algorithms/count.hpp>
#include "test_base.hpp"

TEST(Concat, emptyRange_isEmpty) {
  ivec v;
  auto r = concat(all(v));
  ASSERT_TRUE(r.empty());
}

TEST(Concat, emptyRange_emptyRange_isEmpty) {
  ivec v1, v2;
  auto r = concat(all(v1), all(v2));
  ASSERT_TRUE(r.empty());
}

TEST(Concat, oneElement_notEmpty) {
  ivec v = { 1 };
  auto r = concat(all(v));
  ASSERT_FALSE(r.empty());
}

TEST(Concat, emptyRange_oneElement_notEmpty) {
  ivec v1, v2 = { 1 };
  auto r = concat(all(v1), all(v2));
  ASSERT_FALSE(r.empty());
}

TEST(Concat, oneElement_emptyRange_notEmpty) {
  ivec v1 = { 1 }, v2;
  auto r = concat(all(v1), all(v2));
  ASSERT_FALSE(r.empty());
}

TEST(Concat, emptyRange_oneElement_popFrontMakesEmpty) {
  ivec v1, v2 = { 1 };
  auto r = concat(all(v1), all(v2));
  r.pop_front();
  ASSERT_TRUE(r.empty());
}

TEST(Concat, oneElement_emptyRange_popFrontMakesEmpty) {
  ivec v1 = { 1 }, v2;
  auto r = concat(all(v1), all(v2));
  r.pop_front();
  ASSERT_TRUE(r.empty());
}

TEST(Concat, oneElement_oneElement_popFrontNotEmpty) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_front();
  ASSERT_FALSE(r.empty());
}

TEST(Concat, oneElement_oneElement_popFrontTwiceIsEmpty) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_front();
  r.pop_front();
  ASSERT_TRUE(r.empty());
}

TEST(Concat, oneElement_frontIsElement) {
  ivec v = { 1 };
  auto r = concat(all(v));
  ASSERT_EQ(1, r.front());
}

TEST(Concat, emptyRange_oneElement_frontIsElement) {
  ivec v1, v2 = { 1 };
  auto r = concat(all(v1), all(v2));
  ASSERT_EQ(1, r.front());
}

TEST(Concat, oneElement_oneElement_frontIsFirstElement) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  ASSERT_EQ(1, r.front());
}

TEST(Concat, oneElement_oneElement_popFront_frontIsSecondElement) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_front();
  ASSERT_EQ(2, r.front());
}

TEST(Concat, twoElements_oneElement_popFront_frontIsSecondElement) {
  ivec v1 = { 1, 2 }, v2 = { 3 };
  auto r = concat(all(v1), all(v2));
  r.pop_front();
  ASSERT_EQ(2, r.front());
}

TEST(Concat, twoElements_oneElement_popFrontTwice_frontIsThirdElement) {
  ivec v1 = { 1, 2 }, v2 = { 3 };
  auto r = concat(all(v1), all(v2));
  r.pop_front();
  r.pop_front();
  ASSERT_EQ(3, r.front());
}

TEST(Concat, noElements_oneElement_notEmpty) {
  ivec v1 = { }, v2 = { 1 };
  auto r = concat(all(v1), all(v2));
  ASSERT_FALSE(r.empty());
}

TEST(Concat, emptyRange_oneElement_popBackMakesEmpty) {
  ivec v1, v2 = { 1 };
  auto r = concat(all(v1), all(v2));
  r.pop_back();
  ASSERT_TRUE(r.empty());
}

TEST(Concat, oneElement_emptyRange_popBackMakesEmpty) {
  ivec v1 = { 1 }, v2;
  auto r = concat(all(v1), all(v2));
  r.pop_back();
  ASSERT_TRUE(r.empty());
}

TEST(Concat, oneElement_oneElement_popBackNotEmpty) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_back();
  ASSERT_FALSE(r.empty());
}

TEST(Concat, oneElement_oneElement_popBackTwiceIsEmpty) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_back();
  r.pop_back();
  ASSERT_TRUE(r.empty());
}

TEST(Concat, oneElement_oneElement_popBackAndFrontIsEmpty) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_back();
  r.pop_front();
  ASSERT_TRUE(r.empty());
}

TEST(Concat, oneElement_oneElement_popFrontAndBackIsEmpty) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_front();
  r.pop_back();
  ASSERT_TRUE(r.empty());
}

TEST(Concat, oneElement_backIsElement) {
  ivec v = { 1 };
  auto r = concat(all(v));
  ASSERT_EQ(1, r.back());
}

TEST(Concat, oneElement_emptyRange_backIsElement) {
  ivec v1 = { 1 }, v2;
  auto r = concat(all(v1), all(v2));
  ASSERT_EQ(1, r.back());
}

TEST(Concat, oneElement_oneElement_backIsLastElement) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  ASSERT_EQ(2, r.back());
}

TEST(Concat, oneElement_oneElement_popBack_backIsFirstElement) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_back();
  ASSERT_EQ(1, r.back());
}

TEST(Concat, oneElement_twoElements_popBack_backIsSecondElement) {
  ivec v1 = { 1 }, v2 = { 2, 3 };
  auto r = concat(all(v1), all(v2));
  r.pop_back();
  ASSERT_EQ(2, r.back());
}

TEST(Concat, oneElement_twoElements_popBackTwice_backIsFirstElement) {
  ivec v1 = { 1 }, v2 = { 2, 3 };
  auto r = concat(all(v1), all(v2));
  r.pop_back();
  r.pop_back();
  ASSERT_EQ(1, r.back());
}

TEST(Concat, oneElement_frontPositionRefersToElement) {
  ivec v1 = { 1 };
  auto r = concat(all(v1));
  auto p = r.front_position();
  ASSERT_FALSE(!p);
  ASSERT_EQ(1, *p);
}

TEST(Concat, oneElement_backPositionRefersToElement) {
  ivec v1 = { 1 };
  auto r = concat(all(v1));
  auto p = r.back_position();
  ASSERT_FALSE(!p);
  ASSERT_EQ(1, *p);
}

TEST(Concat, oneElement_oneElement_frontPositionRefersToFirstElement) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  auto p = r.front_position();
  ASSERT_FALSE(!p);
  ASSERT_EQ(1, *p);
}

TEST(Concat, oneElement_oneElement_backPositionRefersToSecondElement) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  auto p = r.back_position();
  ASSERT_FALSE(!p);
  ASSERT_EQ(2, *p);
}

TEST(Concat, oneElement_oneElement_popFront_frontPositionRefersToSecondElement){
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_front();
  auto p = r.front_position();
  ASSERT_FALSE(!p);
  ASSERT_EQ(2, *p);
}

TEST(Concat, oneElement_oneElement_popBack_backPositionRefersToFirstElement) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r.pop_back();
  auto p = r.back_position();
  ASSERT_FALSE(!p);
  ASSERT_EQ(1, *p);
}

TEST(Concat, oneElement_untilFrontIsEmpty) {
  ivec v1 = { 1 };
  auto r = concat(all(v1));
  r = r.until(r.front_position());
  ASSERT_TRUE(r.empty());
}

TEST(Concat, twoElements_oneElement_untilMiddleIsOne) {
  ivec v1 = { 1, 2 }, v2 = { 3 };
  auto r = concat(all(v1), all(v2)), s = r;
  s.pop_front();
  r = r.until(s.front_position());
  ASSERT_FALSE(r.empty());
  ASSERT_EQ(1, count(r));
  ASSERT_EQ(1, r.front());
}

TEST(Concat, twoElements_oneElement_fromMiddleIsTwoThree) {
  ivec v1 = { 1, 2 }, v2 = { 3 };
  auto r = concat(all(v1), all(v2)), s = r;
  s.pop_front();
  r = r.from(s.front_position());
  ASSERT_FALSE(r.empty());
  ASSERT_EQ(2, count(r));
  ASSERT_EQ(2, r.front());
  ASSERT_EQ(3, r.back());
}

TEST(Concat, twoElements_oneElement_untilBoundIsOneTwo) {
  ivec v1 = { 1, 2 }, v2 = { 3 };
  auto r = concat(all(v1), all(v2));
  r = r.until(r.back_position());
  ASSERT_FALSE(r.empty());
  ASSERT_EQ(2, count(r));
  ASSERT_EQ(1, r.front());
  ASSERT_EQ(2, r.back());
}

TEST(Concat, twoElements_oneElement_fromBoundIsThree) {
  ivec v1 = { 1, 2 }, v2 = { 3 };
  auto r = concat(all(v1), all(v2));
  r = r.from(r.back_position());
  ASSERT_FALSE(r.empty());
  ASSERT_EQ(1, count(r));
  ASSERT_EQ(3, r.front());
  ASSERT_EQ(3, r.back());
}

TEST(Concat, noElements_sizeIsZero) {
  ivec v1 = {};
  auto r = concat(all(v1));
  ASSERT_EQ(0, r.size());
}

TEST(Concat, oneElement_sizeIsOne) {
  ivec v1 = { 1 };
  auto r = concat(all(v1));
  ASSERT_EQ(1, r.size());
}

TEST(Concat, oneElement_twoElements_sizeIsThree) {
  ivec v1 = { 1 }, v2 = { 2, 3 };
  auto r = concat(all(v1), all(v2));
  ASSERT_EQ(3, r.size());
}

TEST(Concat, sizeIsCorrectAfterRestoredPosition) {
  ivec v1 = { 1 }, v2 = { 2 };
  auto r = concat(all(v1), all(v2));
  r = r.from(r.back_position());
  ASSERT_EQ(1, r.size());
}

TEST(Concat, oneElement_noElements_twoElements_skipFrontTwo_frontIsThree) {
  ivec v1 = { 1 }, v2 = {}, v3 = { 2, 3 };
  auto r = concat(all(v1), all(v2), all(v3));
  r.skip_front(2);
  ASSERT_EQ(3, r.front());
}

TEST(Concat, twoElements_noElements_oneElement_skipBackTwo_backIsOne) {
  ivec v1 = { 1, 2 }, v2 = {}, v3 = { 3 };
  auto r = concat(all(v1), all(v2), all(v3));
  r.skip_back(2);
  ASSERT_EQ(1, r.back());
}

TEST(Concat, twoElements_noElements_oneElement_indexingIsCorrect) {
  ivec v1 = { 1, 2 }, v2 = {}, v3 = { 3 };
  auto r = concat(all(v1), all(v2), all(v3));
  ASSERT_EQ(1, r[0]);
  ASSERT_EQ(2, r[1]);
  ASSERT_EQ(3, r[2]);
}
